#include "thread.hpp"

namespace libco {

static std::atomic<uint64_t> s_thread_id{1};
static std::atomic<uint64_t> s_thread_count{0};

static thread_local Thread *t_thread          = nullptr;
static thread_local std::string t_thread_name = "UNKNOWN";

Thread *Thread::GetThis() {
    return t_thread;
}

const std::string &Thread::GetName() {
    return t_thread_name;
}

void Thread::SetName(const std::string &name) {
    if (name.empty()) {
        return;
    }
    if (t_thread) {
        t_thread->m_name = name;
    }
    t_thread_name = name;
}

Thread::Thread(std::function<void()> cb) {
    m_cb = cb;
    m_name = "thread_" + std::to_string(s_thread_id);
    m_id = s_thread_id;
    ++s_thread_id;
    ++s_thread_count;
    int rt = pthread_create(&m_thread, nullptr, &Thread::run, this);
    if (rt) {
        LIBCO_LOG_ERROR << "pthread_create thread fail, rt=" << rt
                                  << " name=" << m_name;
        throw std::logic_error("pthread_create error");
    }
    m_semaphore.wait();
}

Thread::Thread(std::function<void()> cb, const std::string &name)
    : m_cb(cb)
    , m_name(name) {
    if (name.empty()) {
        m_name = "UNKNOWN";
    }
    m_id = s_thread_id;
    ++s_thread_id;
    ++s_thread_count;
    int rt = pthread_create(&m_thread, nullptr, &Thread::run, this);
    if (rt) {
        LIBCO_LOG_ERROR << "pthread_create thread fail, rt=" << rt
                                  << " name=" << name;
        throw std::logic_error("pthread_create error");
    }
    m_semaphore.wait();
}

Thread::~Thread() {
    --s_thread_count;
    if (m_thread) {
        pthread_detach(m_thread);
    }
}

void Thread::join() {
    if (m_thread) {
        int rt = pthread_join(m_thread, nullptr);
        if (rt) {
            LIBCO_LOG_ERROR << "pthread_join thread fail, rt=" << rt
                                      << " name=" << m_name;
            throw std::logic_error("pthread_join error");
        }
        m_thread = 0;
    }
}

void *Thread::run(void *arg) {
    Thread *thread = (Thread *)arg;
    t_thread       = thread;
    t_thread_name  = thread->m_name;
    // thread->m_id   = libco::GetThreadId();
    pthread_setname_np(pthread_self(), thread->m_name.substr(0, 15).c_str());

    std::function<void()> cb;
    cb.swap(thread->m_cb);

    thread->m_semaphore.notify();

    cb();
    return 0;
}

} // namespace libco
